using System;
using UnityEngine;
using FairyGUI;
using System.Collections.Generic;
using System.Reflection;

namespace ABA
{
    public enum E_FGUIWIN_ZINDEX
    {
        ROOT,
        VIEW,        //全屏界面
        TOP_VIEW,    //顶部
        BOTTOM_VIEW, //底部
        DIALOG,      //逻辑弹框界面
        GUIDE_VIEW,      //新手引导全屏界面
        GM_VIEW,     //GM view
        LOADING,     //loading
        SYSDIALOG,   //系统强制弹框界面
        COMPONENT,  //组件
        TIPS,        //tips 界面
    }

    public class FGUIWinInfo
    {
        public string cls_name; // 类名 唯一  eg:LoginYoukeWin
        public string pkg_name;
        public string comp_name;
        public E_FGUIWIN_ZINDEX zindex;
        public bool ani;
    }

    public enum E_DLG_MASK_TYPE
    {
        NORMAL,
        CIRCLE,
        MASK2,
        MASK3,
        MASK4,
        EMPTY,  // 没有遮罩，只是屏蔽点击
    }

    public class UISource : IUISource
    {
        FGUIWinInfo _type;
        public UISource(FGUIWinInfo type){
            _type = type;
        }
        /// <summary>
        /// 
        /// </summary>
        public string fileName { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public bool loaded { get; set; }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="callback"></param>
        public void Load(UILoadCallback callback){
        }

        /// <summary>
        /// 取消加载
        /// </summary>
        public void Cancel(){

        }
    }

    public class FGUIWindow : Window
    {
        public static bool closeDlgAni = false;

        // bool _isCenter = false;
        float __width = 0;
        float __height = 0;
        float __yBak = 0;

        GComponent _blockComp;
        Action _blockEventCB;
        protected FGUIWinInfo _winInfo;
        public E_FGUIWIN_ZINDEX winType;
        public string PkgName {
            get {
                return _winInfo.pkg_name;
            }
        }

        // 关闭屏幕适配
        bool _isOpenAdaptive = true;
        public bool OpenAdaptive {
            get {
                return _isOpenAdaptive;
            }
            set {
                _isOpenAdaptive = value;
            }
        }

        public FGUIWinInfo WinInfo{
            get {
                return _winInfo;
            }
        }
        public FGUIWindow(FGUIWinInfo info)
        {
            _winInfo = info;
            this.winType = info.zindex;
        }

        // Show的时候触发的Oninit的方法，并且创建的第一次Show才会触发。
        protected override void OnInit()
        {
            sortingOrder = (int)_winInfo.zindex;
            contentPane = UIPackage.CreateObject(_winInfo.pkg_name, _winInfo.comp_name).asCom;
            contentPane.sortingOrder = 100;

            __width = contentPane.width;
            __height = contentPane.height;

            if(_winInfo.pkg_name != "sys" && _winInfo.pkg_name != "login" &&_isOpenAdaptive == true) // sys ui 不用适配
            {
                AdaptiveAllScreen();
            }
            else{
                // 全屏win, 用GRoot的size
                contentPane.SetSize(GRoot.inst.width, GRoot.inst.height);
                contentPane.AddRelation(GRoot.inst, RelationType.Size);
            }

            GameTimer.inst.CallLater((d) => {  
                AddDataEventListener();
                AddProtoMsgDataEventListener();
            });
        }
        Dictionary<Type, List<int>> eventDic = new Dictionary<Type, List<int>>();
        bool _dataEventOnInit = true;
        public bool dataEventOnInit  //在OnInit 里面调用关闭
        {
            get
            {
                return _dataEventOnInit;
            }
            set
            {
                _dataEventOnInit = value;
            }
        }

        List<int> protoMsgDataList = new List<int>();
        void AddProtoMsgDataEventListener(){
            Type type = Type.GetType("ABA" + "." + _winInfo.cls_name);
            if(type == null) return;
            Type protoMsgDataType = Type.GetType("ABA.ProtoMsgDataEventAttribute");
            if(protoMsgDataType == null) {
                Debug.LogError("ProtoMsgDataEventAttribute not found!!!");
                return;
            }

            MethodInfo[] methods = type.GetMethods();
            foreach (var method in methods)
            {
                var attributes = method.GetCustomAttributes(protoMsgDataType, true);
                if (attributes.Length > 0)
                {
                    foreach (object attribute in attributes)
                    {
                        var dataEvent = attribute;
                        if (null != dataEvent)
                        {
                            var fieldName = protoMsgDataType.GetField("eventNetId").GetValue(dataEvent); // 得到 FGUIWinDataEventAttribute 的 name 值
                            var fieldNameInt = Convert.ToInt32(fieldName);
                            protoMsgDataList.Add(fieldNameInt);

                            Event.inst.AddListener<object>(fieldNameInt, contentPane.id, (data)=>{
                                method.Invoke(this, new object[]{data});
                            });                           
                        }
                    }
                }
            }   
        }
        void AddDataEventListener(){
	        Type type = Type.GetType("ABA" + "." + _winInfo.cls_name);
            if(type == null) return;

            Type dataEventType = Type.GetType("ABA.FGUIWinDataEventAttribute");
            if(dataEventType == null) {
                Debug.LogError("FGUIWinDataEventAttribute not found!!!");
                return;
            }
            Type dataType = Type.GetType("ABA.Data");
            if(dataType == null) {
                Debug.LogError("ABA.Data not found!!!");
                return;
            }
            // Type dataListType = Type.GetType("ABA.DataList");
            // if(dataListType == null) {
            //     Debug.LogError("ABA.DataList not found!!!");
            //     return;
            // }
            // Type dataObjType = Type.GetType("ABA.DataObj");
            // if(dataObjType == null) {
            //     Debug.LogError("ABA.DataObj not found!!!");
            //     return;
            // }

            MethodInfo[] methods = type.GetMethods();
            foreach (var method in methods)
            {
                var attributes = method.GetCustomAttributes(dataEventType, true);
                // 检查方法是否有 MyMethodAttribute 特性
                if (attributes.Length > 0)
                {
                    foreach (object attribute in attributes)
                    {
                        var dataEvent = attribute;
                        if (null != dataEvent)
                        {
                            Type firstParamType = method.GetParameters()[0].ParameterType;  // 得到函数的第一个参数类型，如 DataIntChange

                            var fieldName = dataEventType.GetField("fieldName").GetValue(dataEvent); // 得到 FGUIWinDataEventAttribute 的 name 值
                            var fieldNameInt = Convert.ToInt32(fieldName);
                            var ffType = fieldName.GetType();
                            string fieldStrName = Enum.GetName(ffType, fieldName);

                            // MethodInfo mi = typeof(Event).GetMethod("AddListener", new Type[] {typeof(int), typeof(string), typeof(Action<>) });
                            // MethodInfo[] miii = typeof(Event).GetMethods();
                            
                            // MethodInfo mii = miii[0].MakeGenericMethod(firstParamType);
                            // Action<object> cb = (data) => {
                            //     method.Invoke(this, new object[]{data});
                            // };
                            // Action<string> action = (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), Event.inst, method);
                            // mii.Invoke(Event.inst, new object[] {fieldName, contentPane.id, method});

                            if(firstParamType == typeof(DataIntChange)){
                                if(!eventDic.ContainsKey(typeof(DataIntChange)))    
                                    eventDic.Add(typeof(DataIntChange), new List<int>());
                                eventDic[typeof(DataIntChange)].Add(fieldNameInt);

                                Event.inst.AddListener<DataIntChange>(fieldNameInt, contentPane.id, (data)=>{
                                    method.Invoke(this, new object[]{data});
                                });   
                                
                                if(_dataEventOnInit)
                                {
                                    var getValue = dataType.GetMethod("GetIntValue", BindingFlags.Static | BindingFlags.Public);
                                    var vv = getValue.Invoke(null, new object[]{fieldNameInt});
                                    var d = new DataIntChange(){ newValue = (int)vv, oldValue = (int)vv, fieldName = fieldNameInt, isOnInit = true };
                                    method.Invoke(this, new object[]{d});                                     
                                }
                            }
                            else if(firstParamType == typeof(DataFloatChange)){
                                if(!eventDic.ContainsKey(typeof(DataFloatChange)))    
                                    eventDic.Add(typeof(DataFloatChange), new List<int>());
                                eventDic[typeof(DataFloatChange)].Add(fieldNameInt);

                                Event.inst.AddListener<DataFloatChange>(fieldNameInt, contentPane.id, (data)=>{
                                    method.Invoke(this, new object[]{data});
                                });  

                                if(_dataEventOnInit)
                                {
                                    var getValue = dataType.GetMethod("GetFloatValue", BindingFlags.Static | BindingFlags.Public);
                                    var vv = getValue.Invoke(null, new object[]{fieldNameInt});
                                    var d = new DataFloatChange(){ newValue = (float)vv, oldValue = (float)vv, fieldName = fieldNameInt, isOnInit = true };
                                    method.Invoke(this, new object[]{d});                                     
                                }  
                            }
                            else if(firstParamType == typeof(DataBoolChange)){
                                if(!eventDic.ContainsKey(typeof(DataBoolChange)))    
                                    eventDic.Add(typeof(DataBoolChange), new List<int>());
                                eventDic[typeof(DataBoolChange)].Add(fieldNameInt);

                                Event.inst.AddListener<DataBoolChange>(fieldNameInt, contentPane.id, (data)=>{
                                    method.Invoke(this, new object[]{data});
                                });  

                                if(_dataEventOnInit)
                                {
                                    var getValue = dataType.GetMethod("GetBoolValue", BindingFlags.Static | BindingFlags.Public);
                                    var vv = getValue.Invoke(null, new object[]{fieldNameInt});
                                    var d = new DataBoolChange(){ newValue = (bool)vv, oldValue = (bool)vv, fieldName = fieldNameInt, isOnInit = true };
                                    method.Invoke(this, new object[]{d});                                     
                                }   
                            }
                            else if(firstParamType == typeof(DataListChange)){
                                if(!eventDic.ContainsKey(typeof(DataListChange)))    
                                    eventDic.Add(typeof(DataListChange), new List<int>());
                                eventDic[typeof(DataListChange)].Add(fieldNameInt);

                                Event.inst.AddListener<DataListChange>(fieldNameInt, contentPane.id, (data)=>{
                                    method.Invoke(this, new object[]{data});
                                });  

                                if(_dataEventOnInit)
                                {
                                    var getValue = dataType.GetProperty(fieldStrName, BindingFlags.Static | BindingFlags.Public).GetValue(null);
                                    var d = new DataListChange(){ newValue = getValue, oldValue = getValue, fieldName = fieldNameInt, isOnInit = true };
                                    method.Invoke(this, new object[]{d});                                     
                                }
                            }
                            else if(firstParamType == typeof(DataStringChange)){
                                if(!eventDic.ContainsKey(typeof(DataStringChange)))    
                                    eventDic.Add(typeof(DataStringChange), new List<int>());
                                eventDic[typeof(DataStringChange)].Add(fieldNameInt);

                                Event.inst.AddListener<DataStringChange>(fieldNameInt, contentPane.id, (data)=>{    
                                    method.Invoke(this, new object[]{data});
                                });

                                if(_dataEventOnInit)
                                {
                                    var getValue = dataType.GetMethod("GetStringValue", BindingFlags.Static | BindingFlags.Public);
                                    var vv = getValue.Invoke(null, new object[]{fieldNameInt});
                                    var d = new DataStringChange(){ newValue = (string)vv, oldValue = (string)vv, fieldName = fieldNameInt, isOnInit = true };
                                    method.Invoke(this, new object[]{d});                                     
                                }
                            }
                            else if(firstParamType == typeof(DataLongChange)){
                                if(!eventDic.ContainsKey(typeof(DataLongChange)))    
                                    eventDic.Add(typeof(DataLongChange), new List<int>());
                                eventDic[typeof(DataLongChange)].Add(fieldNameInt);

                                Event.inst.AddListener<DataLongChange>(fieldNameInt, contentPane.id, (data)=>{
                                    method.Invoke(this, new object[]{data});
                                });

                                if(_dataEventOnInit)
                                {
                                    var getValue = dataType.GetMethod("GetLongValue", BindingFlags.Static | BindingFlags.Public);
                                    var vv = getValue.Invoke(null, new object[]{fieldNameInt});
                                    var d = new DataLongChange(){ newValue = (long)vv, oldValue = (long)vv, fieldName = fieldNameInt, isOnInit = true };
                                    method.Invoke(this, new object[]{d});                                     
                                }
                            }
                            else if(firstParamType == typeof(DataDoubleChange)){
                                if(!eventDic.ContainsKey(typeof(DataDoubleChange)))    
                                    eventDic.Add(typeof(DataDoubleChange), new List<int>());
                                eventDic[typeof(DataDoubleChange)].Add(fieldNameInt);

                                Event.inst.AddListener<DataDoubleChange>(fieldNameInt, contentPane.id, (data)=>{
                                    method.Invoke(this, new object[]{data});
                                });

                                if(_dataEventOnInit)
                                {
                                    var getValue = dataType.GetMethod("GetDoubleValue", BindingFlags.Static | BindingFlags.Public);
                                    var vv = getValue.Invoke(null, new object[]{fieldNameInt});
                                    var d = new DataDoubleChange(){ newValue = (double)vv, oldValue = (double)vv, fieldName = fieldNameInt, isOnInit = true };
                                    method.Invoke(this, new object[]{d});                                     
                                }
                            }
                            else if(firstParamType == typeof(DataObjChange)){
                                if(!eventDic.ContainsKey(typeof(DataObjChange)))    
                                    eventDic.Add(typeof(DataObjChange), new List<int>());
                                eventDic[typeof(DataObjChange)].Add(fieldNameInt);

                                Event.inst.AddListener<DataObjChange>(fieldNameInt, contentPane.id, (data)=>{
                                    method.Invoke(this, new object[]{data});
                                });

                                if(_dataEventOnInit)
                                {
                                    var getValue = dataType.GetProperty(fieldStrName, BindingFlags.Static | BindingFlags.Public).GetValue(null);
                                    var d = new DataObjChange(){ newValue = getValue, oldValue = getValue, fieldName = fieldNameInt, isOnInit = true };
                                    method.Invoke(this, new object[]{d});                                     
                                }
                            }
                        }
                    }  
                }
            }   
        }
        void RemoveDataEventListener(){
            foreach(var item in eventDic){
                foreach(var fieldName in item.Value){
                    if(item.Key == typeof(DataIntChange))
                        Event.inst.RemoveListener<DataIntChange>(fieldName, contentPane.id);
                    else if(item.Key == typeof(DataFloatChange))
                        Event.inst.RemoveListener<DataFloatChange>(fieldName, contentPane.id);
                    else if(item.Key == typeof(DataBoolChange))
                        Event.inst.RemoveListener<DataBoolChange>(fieldName, contentPane.id);
                    else if(item.Key == typeof(DataListChange)) 
                        Event.inst.RemoveListener<DataListChange>(fieldName, contentPane.id);
                    else if(item.Key == typeof(DataStringChange))
                        Event.inst.RemoveListener<DataStringChange>(fieldName, contentPane.id);
                    else if(item.Key == typeof(DataLongChange))
                        Event.inst.RemoveListener<DataLongChange>(fieldName, contentPane.id);
                    else if(item.Key == typeof(DataDoubleChange))
                        Event.inst.RemoveListener<DataDoubleChange>(fieldName, contentPane.id);
                    else if(item.Key == typeof(DataObjChange))
                        Event.inst.RemoveListener<DataObjChange>(fieldName, contentPane.id);
                }
            }
        }

        void RemoveProtoMsgDataEventListener(){
            foreach(var item in protoMsgDataList){
                Event.inst.RemoveListener<DataBoolChange>(item, contentPane.id);
            }
        }

        public void AdaptiveAllScreen(){
            int safaAreaOffset = FGUIAdaptive.inst.GetSafaAreaOffset();
            contentPane.SetSize(GRoot.inst.width, GRoot.inst.height-safaAreaOffset);
            contentPane.AddRelation(GRoot.inst, RelationType.Size);
            __yBak = contentPane.y;
            contentPane.y = FGUIAdaptive.inst.GetContentPaneYoffset();
            // Debug.Log($"FGUIWindow AdaptiveAllScreen safaAreaOffset = {safaAreaOffset} contentPane.y = {contentPane.y}");
            var bgbg = contentPane.GetChild("bgbg"); // 全屏背景适配让其居中
            if(bgbg != null){
                bgbg.y -= contentPane.y / 2;
            }  
        }

        // 窗口每次显示时都得改变层级
        public new void Show()
        {
            sortingOrder = (int)_winInfo.zindex;
            base.Show();       
        }

        // 
        public void ShowForZindex(int zIndex)
        {
            sortingOrder = zIndex;
            base.Show();       
        }

        // 窗口每次隐藏时都得改变层级
        public new void Hide()
        {
            base.Hide();      
        }

        // 非全屏UI要在OnInit里面调用Center
        public new void Center()
        {
            // _isCenter = true;
            // 如果是对话框居中，则把size 设置回来
            contentPane.SetSize(__width, __height);
            contentPane.AddRelation(GRoot.inst, RelationType.Size);  
            contentPane.y = __yBak;  // 对话框不用适配，故要还原
            base.Center(); 

            // 默认打开
            SetBlockEvent(true, null);
        }


        bool _kongBaiBlockEventMask = false;
        public bool KongBaiBlockEventMask // 空白遮罩方式
        {
            get
            {
                return _kongBaiBlockEventMask;
            }
            set
            {
                _kongBaiBlockEventMask = value;
                if(_blockComp != null) {
                    _blockComp.GetController("type").selectedIndex = (int)(_kongBaiBlockEventMask ? E_DLG_MASK_TYPE.EMPTY: _blockEventType);
                }
            }
        }

        E_DLG_MASK_TYPE _blockEventType = E_DLG_MASK_TYPE.NORMAL;
        // 主要用于对话框
        // 对话框屏蔽空白处事件点击 true 打开， false关闭，cb: 是点击空白回调
        // 外部如果要设置点击空白处关闭，可以调用此函数
        public void SetBlockEvent(bool flag, Action cb, E_DLG_MASK_TYPE blockEventType = E_DLG_MASK_TYPE.NORMAL)
        {
            _blockEventType = blockEventType;

            if(cb != null) _blockEventCB = cb;
            if(flag == true){
                if(_blockComp != null) {
                    _blockComp.GetController("type").selectedIndex = (int)(_kongBaiBlockEventMask ? E_DLG_MASK_TYPE.EMPTY: _blockEventType);
                    return;
                }

                _blockComp = UIPackage.CreateObject(CommData.WIN_SYS_BLOCK.pkg_name, CommData.WIN_SYS_BLOCK.comp_name).asCom;
                AddChild(_blockComp);

                _blockComp.onClick.Set(()=>{   
                    if(_blockEventCB != null){
                        _blockEventCB(); 
                    }                        
                    //LogManager.LogInfo("UIWindow SetBlockEvent ！！！");
                });

                _blockComp.sortingOrder = 0;
                _blockComp.GetController("type").selectedIndex = (int)(_kongBaiBlockEventMask ? E_DLG_MASK_TYPE.EMPTY: _blockEventType);
                _blockComp.SetSize(GRoot.inst.width, GRoot.inst.height);
                _blockComp.AddRelation(GRoot.inst, RelationType.Size);    
                _blockComp.SetXY(-x, -y);
            }
            else{
                if(_blockComp != null){
                    _blockComp.RemoveFromParent();
                    _blockComp.Dispose();
                    _blockComp = null;
                }
            }
        }

        public bool IsHasBlockEventMask()
        {
            if(_blockComp != null && _blockComp.visible == true){
                if(_blockComp.GetController("type").selectedIndex == 5){ // 5 是空白的遮罩，但是会阻挡事件
                    return false;
                }

                return true;
            }

            return false;
        }

        // 具体窗口重写，在这里请求服务器数据，然后回调刷新UI,不管是不是第一次Show,都会在Show()后调用此接口
        // 如果是构造函数里面有带参数，那这里同样也会带参数
        public virtual void InitAfterShow()
        {

        }

        bool _autoShow = true;
        public bool autoFirstShow  // FIXME:longer如果是要发送请求到服务器，等待请求回来再show, 可以在构造函数autoFirstShow = false, 自己回调里面调用Show
        {
            get
            {
                return _autoShow;
            }
            set
            {
                _autoShow = value;
            }
        }

        // 显示动画
        override protected void DoShowAnimation()
        {
            if(_blockComp != null){
                _blockComp.visible = true;
            }

            if (_winInfo.ani == false || closeDlgAni == true)
            {
                OnShown();
                return;
            }

            this.alpha = 1;
            SetScale(1.0f, 1.0f);
            SetPivot(0.5f, 0.5f);
            // alpha = 1;
            var aa = TweenScale(new Vector2(1.02f, 1.02f), 0.04f).OnComplete(()=>{
                var bb = TweenScale(new Vector2(0.98f, 0.98f), 0.06f).OnComplete(()=>{
                    var cc = TweenScale(new Vector2(1.0f, 1.0f), 0.05f).OnComplete(OnShown);
                    cc.SetEase(EaseType.Linear);
                });
                bb.SetEase(EaseType.Linear);
                // bb.SetEasePeriod(10);
                // bb.SetEaseOvershootOrAmplitude(100);
                // bb.SetEase(EaseType.BounceOut);
            });
            // var aa = TweenScale(new Vector2(1.0f, 1.0f), 0.2f).OnComplete(OnShown);
            // aa.SetEasePeriod(100);
            aa.SetEase(EaseType.Linear);
        }

        // 关闭动画
        override protected void DoHideAnimation()
        {
            if(_blockComp != null){
                _blockComp.visible = false;
            }

            if (_winInfo.ani == false || closeDlgAni == true)
            {
                HideImmediately();
                return;
            }

            var aa = TweenScale(new Vector2(1.05f, 1.05f), 0.05f).OnComplete(()=>{
                var bb = TweenScale(new Vector2(0.6f, 0.6f), 0.02f).OnComplete(HideImmediately);
                bb.SetEase(EaseType.Linear);
                var cc = TweenFade(0, 0.02f);
                cc.SetEase(EaseType.Linear);

            });
            aa.SetEase(EaseType.Linear);

            // scaleY = 1;
            // TweenScaleY(0, 2f).SetEase(EaseType.SineInOut).OnComplete(HideImmediately);
            // TweenMoveX(-width, 1f).SetDelay(1f).SetEase(EaseType.QuadIn);
        }

        // 窗口每次显示时的逻辑可以写这里，子类可以重写，相当enable
        override protected void OnShown()
        {

        }

        // 由于现在的窗口都是隐藏不销毁的，一些耗时定时器操作，最好在隐藏的时候停止，不然会一直在后台跑
        // 窗口每次隐藏时的逻辑可以写这里，子类可以重写，相当disable
        override protected void OnHide()
        {

        }

        override public void Dispose(){
            base.Dispose();
            this.OnDispose();
        }

        // 销毁窗口的回调，可以移除事件注册，与构造函数一一对应
        protected virtual void OnDispose(){
            RemoveDataEventListener();
            RemoveProtoMsgDataEventListener();
        }

        public virtual void AniInOnComplete(){

        }
    }
}
